package hrd

import "sync"
import "strings"
import "io/ioutil"
import "strconv"

var lock sync.RWMutex
var mapPositions map[uint] []uint
func init() {
  mapPositions = make(map[uint] []uint)
}

type Item interface {
  Position() uint
  Mask() uint32
  Hash() uint64
  NextBoradItemMap() map[uint32] []Item
  Next() []Item
  Char() string
  Lock()
  Unlock()
  RLock()
  RUnlock()
}

func NextPositions(mask uint32, position uint) []uint {
  var nextPositions []uint
  if mask & 0x11111 == 0 {
    nextPositions = append(nextPositions, position - 1)
  }
  if mask & 0x88888 == 0 {
    nextPositions = append(nextPositions, position + 1)
  }
  if mask & 0xf == 0 {
    nextPositions = append(nextPositions, position - 4)
  }
  if mask & 0xf0000 == 0 {
    nextPositions = append(nextPositions, position + 4)
  }
  return nextPositions
}

type Base struct {
  *sync.RWMutex
  position uint
  nextBoardItemMap map[uint32] []Item
  next []Item
}

func NewBase(position uint) Base {
  var lock sync.RWMutex
  return Base{&lock, position, make(map[uint32] []Item), []Item{}}
}

func (item *Base) Position() uint { return item.position }

func (item *Base) NextBoradItemMap() map[uint32] []Item{ return item.nextBoardItemMap }

func (item *Base) Next() []Item { return item.next }

type Box struct { Base }

func (item *Box) Mask() uint32 { return 51 << item.position }

func (item *Box) Hash() uint64 { return uint64(item.position) << 60 }

func (item *Box) Char() string { return "B" }


type VerticalBar struct { Base }

func (item *VerticalBar) Mask() uint32 { return 17 << item.position }

func (item *VerticalBar) Hash() uint64 { return 1 << (item.position + 40) }

func (item *VerticalBar) Char() string { return "V" }


type HorizontalBar struct { Base }

func (item *HorizontalBar) New(position uint) Item { return arHorizontals[position] }

func (item *HorizontalBar) Mask() uint32 { return 3 << item.position}

func (item *HorizontalBar) Hash() uint64 { return 1 << (item.position + 20)}

func (item *HorizontalBar) Char() string { return "H" }


type Dot struct { Base }

func (item *Dot) Mask() uint32 { return 1 << item.position }

func (item *Dot) Hash() uint64 { return 1 << item.position }

func (item *Dot) Char() string { return "D" }


func Positions(base uint) []uint {
  lock.RLock()
  positions, present := mapPositions[base]
  lock.RUnlock()
  if present {return positions}
  for i := 0; i < 20; i++ {
    if ((1 << uint(i)) & base) != 0 {
      positions = append(positions, uint(i))
    }
  }
  lock.Lock()
  mapPositions[base] = positions
  lock.Unlock()
  return positions
}

func BoxPosition(board uint64) uint { return uint(board >> 60) }

func VerticalBarPositions(board uint64) []uint { return Positions(uint(board >> 40) & 0xfffff) }

func HorizontalBarPositions(board uint64) []uint { return Positions(uint(board >> 20) & 0xfffff) }

func DotPositions(board uint64) []uint { return Positions(uint(board) & 0xfffff) }

func BoardItems(board uint64) []Item {
  items := []Item {}
  items = append(items, arBoxes[BoxPosition(board)])
  for _, v := range VerticalBarPositions(board) {
    items = append(items, arVerticals[v])
  }
  for _, v := range HorizontalBarPositions(board) {
    items = append(items, arHorizontals[v])
  }
  for _, v := range DotPositions(board) {
    items = append(items, arDots[v])
  }
  return items
}

func NewBoard(b uint, vs []uint, hs []uint, ds []uint) uint64 {
  var board uint64

  box := arBoxes[b]
  board = box.Hash()
  for _, v := range vs {
    vb := arVerticals[v]
    board |= vb.Hash()
  }
  for _, v := range hs {
    hb := arHorizontals[v]
    board |= hb.Hash()
  }
  for _, v := range ds {
    db := arDots[v]
    board |= db.Hash()
  }

  return board
}

func NextItems(item Item, mask uint32, continuous bool) []Item {
  mapNexts := item.NextBoradItemMap()
  item.RLock()
  nextSquares, present := mapNexts[mask]
  item.RUnlock()
  if present {return nextSquares}
  for _, next := range item.Next() {
    if (next.Mask() & mask) != 0 { continue }
    nextSquares = append(nextSquares, next)
    if continuous {
      for _, next := range(NextItems(next, mask, false)) {
        if next.Position() != item.Position() { nextSquares = append(nextSquares, next) }
      }
    }
  }
  
  item.Lock()
  defer item.Unlock()
  if continuous { mapNexts[mask] = nextSquares }
  SaveItemMap(item)
  return nextSquares
}

func SaveItemMap(item Item) {
  var sm []string
  for k, items := range item.NextBoradItemMap() {
    si := []string {strconv.Itoa(int(k))}
    for _, item := range items {
      si = append(si, ",", strconv.Itoa(int(item.Position())))
    }
    sm = append(sm, strings.Join(si, ""))
  }
  s := strings.Join(sm, "\n")
  fn := "data\\" + item.Char() + strconv.Itoa(int(item.Position()))
  ioutil.WriteFile(fn , []byte(s), 0644)  
}

func LoadItemMap(item Item) {
  fn := "data\\" + item.Char() + strconv.Itoa(int(item.Position()))  
  b, err := ioutil.ReadFile(fn)
  if err != nil { return }
  mapItem := make(map[uint] Item)
  mapNext := item.NextBoradItemMap()
  for _, n := range item.Next() { mapItem[n.Position()] = n }
  for _, line := range strings.Split(string(b), "\n") {
    var key uint32
    nexts := make([]Item, 0)
    for index, v := range strings.Split(line, ",") {
      value, _ := strconv.Atoi(v)
      if index == 0 {
        key = uint32(value)
      } else if mapItem[uint(value)] != nil {        
        nexts = append(nexts, mapItem[uint(value)])
      }
    }
    item.Lock()
    mapNext[key] = nexts
    item.Unlock()
  }
}

var arBoxes [20]*Box
var arVerticals [20]*VerticalBar
var arHorizontals [20]*HorizontalBar
var arDots [20]*Dot

func init(){
  for x := 0; x < 3; x++ {
    for y := 0; y < 4; y++ {
      i := uint(y * 4 + x)
      arBoxes[i] = &Box{NewBase(i)}
    }
  }
  for _, item := range arBoxes {
    if item != nil {
      for _, position := range NextPositions(item.Mask(), item.Position()) {
        item.next = append(item.next, arBoxes[position])
      }
      LoadItemMap(item)
    }
  }

  for x := 0; x < 4; x++ {
    for y := 0; y < 4; y++ {
      i := uint(y * 4 + x)
      arVerticals[i] = &VerticalBar{NewBase(i)}
    }
  }
  for _, item := range arVerticals {
    if item != nil {
      for _, position := range NextPositions(item.Mask(), item.Position()) {
        item.next = append(item.next, arVerticals[position])
      }
      LoadItemMap(item)
    }
  }

  for x := 0; x < 3; x++ {
    for y := 0; y < 5; y++ {
      i := uint(y * 4 + x)
      arHorizontals[i] = &HorizontalBar{NewBase(i)}
    }
  }
  for _, item := range arHorizontals {
    if item != nil {
      for _, position := range NextPositions(item.Mask(), item.Position()) {
        item.next = append(item.next, arHorizontals[position])
      }
      LoadItemMap(item)
    }
  }

  for i := 0; i < 20; i++ {arDots[i] = &Dot{NewBase(uint(i))}}
  for _, item := range arDots {
    for _, position := range NextPositions(item.Mask(), item.Position()) {
      item.next = append(item.next, arDots[position])
    }
    LoadItemMap(item)
  }
}